package com.xadrez.modelo;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class Peca implements Cloneable {

    private Tabuleiro tabuleiro;
    private transient Jogador jogador;
    private boolean ativa;
    private Posicao posicaoNoTabuleiro;

    public Peca() {
    }

    public Peca(Jogador jogador, Tabuleiro tabuleiro) {
        this.jogador = jogador;
        ativa = true;
        this.tabuleiro = tabuleiro;
    }

    // <editor-fold defaultstate="collapsed" desc="MÉTODOS DE MOVIMENTAÇÃO">
    //Desloca a peça para alguma posição. ou para COMER ou para MOVER
    public boolean deslocaPeca(Posicao posicao) {
        boolean moveu = false;
        int x = posicao.getX();
        int y = posicao.getY();
        tabuleiro.plusEmpate();

        if(posicao.isRoquePequeno()){
            tabuleiro.getQuadrados()[x][6] = tabuleiro.getQuadrados()[x][posicao.getY()];
            tabuleiro.getQuadrados()[x][5] = tabuleiro.getQuadrados()[x][7];
            
            tabuleiro.getQuadrados()[x][y] = null;
            tabuleiro.getQuadrados()[x][7] = null;
            
            tabuleiro.getQuadrados()[x][6].getPosicaoNoTabuleiro().setX(x);
            tabuleiro.getQuadrados()[x][6].getPosicaoNoTabuleiro().setY(6);
            tabuleiro.getQuadrados()[x][5].getPosicaoNoTabuleiro().setX(x);
            tabuleiro.getQuadrados()[x][5].getPosicaoNoTabuleiro().setY(5);
            
            return true;
        }else if(posicao.isRoqueGrande()){
            tabuleiro.getQuadrados()[posicao.getX()][2] = tabuleiro.getQuadrados()[posicao.getX()][posicao.getY()];
            tabuleiro.getQuadrados()[posicao.getX()][3] = tabuleiro.getQuadrados()[posicao.getX()][0];
            tabuleiro.getQuadrados()[posicao.getX()][posicao.getY()] = null;
            tabuleiro.getQuadrados()[posicao.getX()][0] = null;
            
            tabuleiro.getQuadrados()[x][2].getPosicaoNoTabuleiro().setX(x);
            tabuleiro.getQuadrados()[x][2].getPosicaoNoTabuleiro().setY(2);
            tabuleiro.getQuadrados()[x][3].getPosicaoNoTabuleiro().setX(x);
            tabuleiro.getQuadrados()[x][3].getPosicaoNoTabuleiro().setY(3);
            
            
            return true;
        }
        
        
        //Ele só pode mover a peça para uma posicoes que esteja dentro do alcance da mesma
        if (podeMover(posicao)) {
            Peca pecaNaPosicaoXY = tabuleiro.getQuadrados()[x][y];

            if (pecaNaPosicaoXY != null) {
                if (pecaNaPosicaoXY.getJogador().equals(tabuleiro.getJogadorAdversario())) {
                    come(pecaNaPosicaoXY);
                    moveu = true;
                    tabuleiro.zeraEmpate();
                }
            } else {
                move(posicao);
                moveu = true;
            }
        }

        return moveu;
    }

    private void move(Posicao posicao) {

        int i = this.getPosicaoNoTabuleiro().getX();
        int j = this.getPosicaoNoTabuleiro().getY();
        int x = posicao.getX();
        int y = posicao.getY();

        //Posicionamos no novo quadrado
        tabuleiro.getQuadrados()[x][y] = this;
        //Removemos peca do quadrado antigo
        tabuleiro.getQuadrados()[i][j] = null;
        //Atualizamos o atributo posicao da peca
        this.setPosicaoNoTabuleiro(posicao);

        //Se for peao devemos setar que gastou seu primeiro movimento
        if (this instanceof Peao) {
            tabuleiro.zeraEmpate();
            if (((Peao) this).isPrimeiroMovimento()) {
                ((Peao) this).setPrimeiroMovimento(false);
            }
        }if ((this instanceof Rei) && (((Rei) this).isPrimeiroMovimento())) {
            ((Rei) this).setPrimeiroMovimento(false);
        }if ((this instanceof Torre) && (((Torre) this).isPrimeiroMovimento())){
            ((Torre) this).setPrimeiroMovimento(false);
        }
    }

    private void come(Peca pecaASerComida) {

        int x = pecaASerComida.getPosicaoNoTabuleiro().getX();
        int y = pecaASerComida.getPosicaoNoTabuleiro().getY();
        tabuleiro.getQuadrados()[x][y] = this;

        x = this.getPosicaoNoTabuleiro().getX();
        y = this.getPosicaoNoTabuleiro().getY();
        tabuleiro.getQuadrados()[x][y] = null;


        this.setPosicaoNoTabuleiro(pecaASerComida.getPosicaoNoTabuleiro());
        getTabuleiro().getJogadorAdversario(jogador).getPecas().remove(pecaASerComida);

        //Se for peao devemos setar que gastou seu primeiro movimento
        if ((this instanceof Peao) && (((Peao) this).isPrimeiroMovimento())) {
            ((Peao) this).setPrimeiroMovimento(false);
        }if ((this instanceof Rei) && (((Rei) this).isPrimeiroMovimento())) {
            ((Rei) this).setPrimeiroMovimento(false);
        }if ((this instanceof Torre) && (((Torre) this).isPrimeiroMovimento())){
            ((Torre) this).setPrimeiroMovimento(false);
        }
        
        
    }
    //Esse método verifica se a peca pode ser movida para a casa (x,y) do tabuleiro
    
    public abstract List<Posicao> caminhoAteOReiAdversario();
    
    private boolean podeMover(Posicao posicao) {
        boolean confirma = false;
        List<Posicao> lista = new ArrayList<Posicao>();

        lista = this.recuperaMovimentosPossiveis();

        if (lista != null) {
            int i = 0;
            while (i < lista.size() && !confirma) {
                if (lista.get(i).getX() == posicao.getX()
                        && lista.get(i).getY() == posicao.getY()) {
                    confirma = true;
                }
                i++;
            }
        }

        return confirma;
    }
    //Verfica se ao movimentar a peca colocara o rei em checkMate
    public boolean deixaReiEmCheck(Posicao posicao) {
        boolean estaAmeacado = true;
        List<Posicao> listaDeAmeacas = getJogador().getReiDoJogador().getListaDeMovimentosQueAmeacam();

        if (listaDeAmeacas != null && !listaDeAmeacas.isEmpty()) {
            int i = 0;
            while (estaAmeacado && i < listaDeAmeacas.size()) {
                if (listaDeAmeacas.get(i).equals(posicao)) {
                    estaAmeacado = false;
                }
                i++;
            }
        } else if (!reiDesprotegido(getPosicaoNoTabuleiro())) {
            estaAmeacado = false;
        }

        return estaAmeacado;
    }

    private boolean reiDesprotegido(Posicao posicao) {
        boolean deixa = false;
        List<Peca> pecasDoAdversario = getTabuleiro().getJogadorAdversario(getJogador()).getPecas();
        int i = 0;
        while (!deixa && i < pecasDoAdversario.size()) {
            Peca peca = pecasDoAdversario.get(i);
            if (peca.isAtiva() 
                    && ((peca instanceof Torre) || (peca instanceof Bispo) || (peca instanceof Rainha))) {
                List<Posicao> listaPosicoes = peca.caminhoAteOReiAdversario();
                int j = 0;
                while (!deixa && j < listaPosicoes.size()) {
                    if (listaPosicoes.get(j).equals(posicao)) {
                        deixa = true;
                    }
                    j++;
                }
            }
            i++;
        }
        return deixa;
    }
    // </editor-fold>
    //Recupera uma lista de movimentos posiveis para determinada peca
    public abstract List<Posicao> recuperaMovimentosPossiveis();

    //Recupera uma lista de movimentos que ameacam o rei
    public abstract List<Posicao> recuperaMovimentosParaCheck();
    
    public Tabuleiro getTabuleiro(){
        return tabuleiro;
    }
    
    // <editor-fold defaultstate="collapsed" desc="GETs e SETs">
    public Jogador getJogador() {
        return jogador;
    }

    public void setJogador(Jogador jogador) {
        this.jogador = jogador;
    }

    public boolean isAtiva() {
        return ativa;
    }

    public void setAtiva(boolean ativa) {
        this.ativa = ativa;
    }

    public Posicao getPosicaoNoTabuleiro() {
        return posicaoNoTabuleiro;
    }

    public void setPosicaoNoTabuleiro(Posicao posicaoNoTabuleiro) {
        this.posicaoNoTabuleiro = posicaoNoTabuleiro;
    }

    public boolean isPosicaoDentroDosLimites(Posicao posicao) {
        boolean confirma = false;
        if ((posicao.getX() < 8) && (posicao.getX() >= 0) && (posicao.getY() < 8) && posicao.getY() >= 0) {
            confirma = true;
        }
        return confirma;
    }

    public void setTabuleiro(Tabuleiro tabuleiro) {
        this.tabuleiro = tabuleiro;
    }

    // </editor-fold>
    @Override
    public Peca clone() {
        Peca temp = null;
        try {
            temp = (Peca) super.clone();
        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(Peca.class.getName()).log(Level.SEVERE, null, ex);
        }
        return temp;
    }
}
